#include "NetworkObject.h"

#include "EventHash.h"
#include "EventNode.h"
#include "EventObject.h"

#include <arpa/inet.h>
#include <fcntl.h>
#include <libintl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#define _(String) gettext(String)

static int	     *control_socket = NULL;
static bool	      quit_flag	     = false;
static struct timeval timeout;

struct _NetworkObject
{
	EventObject parent_instance;

	/* 网络任务表 */
	// EventHash *task_table;
	/* 处理 accept 信号的回调函数 */
	void	     (*accept_handler)(EventObject *, ExecuteQueue *);
	EventObject *handler_object;

	/* 返回消息队列 */
	// ExecuteQueue  *q_head;
	// ExecuteQueue **q_end;

	/* 绑定本机的套接字 */
	int local_socket;
	/* epoll */
	int epollfd;

	/* 是否已初始化？ */
	bool is_inited;

	/* 是否正在执行发送任务 */
	// bool is_sending;

	/* 队列是否已上锁 */
	// bool is_queuing;
};

static void
network_object_finalize(NetworkObject *self)
{
	if (self->local_socket != -1)
		close(self->local_socket);
	if (self->epollfd != -1)
		close(self->epollfd);
	// event_object_unref(self->q_head);
}

NetworkObject *
network_object_new(void)
{
	NetworkObject *object = MALLOC_NEW(NetworkObject);

	event_object_init(EVENT_OBJECT(object));
	event_object_set_finalize(object, FINALIZE(network_object_finalize));

	object->is_inited    = false;
	object->local_socket = -1;
	object->epollfd	     = -1;
	control_socket	     = &object->local_socket;
	// object->q_head         = NULL;
	// object->q_end          = NULL;
	// object->is_sending     = false;
	// object->is_queuing     = false;
	object->accept_handler = NULL;
	object->handler_object = NULL;
	//
	timeout.tv_sec	= 0;
	timeout.tv_usec = 200000;

	return object;
}

static void
network_quit_with_signal(int sig)
{
	if (!control_socket) {
		printf(_("Socket not set\n"));
		return;
	}
	if (*control_socket == -1) {
		printf(_("The socket seems to have exited ......\n"));
		return;
	}
	quit_flag = true;
	close(*control_socket);
	*control_socket = -1;
}

void
network_object_init(NetworkObject *self)
{
	struct epoll_event ev;

	return_if_fail(self != NULL, network_object_init);
	if (self->is_inited)
		return;
	if (signal(SIGINT, network_quit_with_signal) == SIG_ERR) {
		perror("signal");
		return;
	}
	self->local_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (self->local_socket == -1) {
		perror("socket");
		return;
	}
	int opt = 1;
	if (setsockopt(self->local_socket, SOL_SOCKET, SO_REUSEADDR, &opt,
		       sizeof(opt)) < 0) {
		perror("setsockeopt");
		return;
	}
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port	= htons(N_OBJ_PORT);
	inet_pton(AF_INET, "0.0.0.0", &addr.sin_addr);
	if (bind(self->local_socket, (struct sockaddr *)&addr,
		 sizeof(struct sockaddr)) == -1) {
		perror("bind");
		return;
	}
	if (listen(self->local_socket, 100) == -1) {
		perror("listen");
		return;
	}
	self->epollfd = epoll_create1(0);
	if (self->epollfd == -1) {
		perror("epoll_create1");
		return;
	}
	ev.events  = EPOLLIN;
	ev.data.fd = self->local_socket;
	if (epoll_ctl(self->epollfd, EPOLL_CTL_ADD, self->local_socket, &ev) ==
	    -1) {
		perror("epoll_ctl: self->local_socket");
		return;
	}
	self->is_inited = true;
}

int
network_object_loop_wait(NetworkObject *self)
{
	static char		  msg_buffer[8192];
	static struct epoll_event events[MAX_EPOLL_EVENT], ev;
	static struct sockaddr	  address;
	static socklen_t	  addr_len = sizeof(struct sockaddr);
	//
	EventRaw     *raw_object;
	ExecuteQueue *q_node;
	EventList    *list;
	epointer      raw;
	ssize_t	      bytes;
	int	      accept_sock, catched_nums, index = 0;

	return_val_if_fail(self != NULL && self->is_inited != false,
			   _network_object_accept, 0);
	catched_nums = epoll_wait(self->epollfd, events, MAX_EPOLL_EVENT, -1);
	if (catched_nums == -1) {
		perror("epoll_wait");
		return 0;
	}

re_loop:
	if (index >= catched_nums)
		return 1;
	if (events[index].data.fd == self->local_socket) {
		accept_sock = accept(self->local_socket, &address, &addr_len);
		event_debug(
			NetworkObject, network_object_loop_wait, "接收: '%s'",
			inet_ntop(AF_INET,
				  &((struct sockaddr_in *)&address)->sin_addr,
				  msg_buffer, addr_len));
		if (accept_sock == -1) {
			perror("accept");
			return 0;
		}
		// fcntl(accept_sock, F_SETFL, fcntl(accept_sock, F_GETFL));
		ev.events  = EPOLLIN;
		ev.data.fd = accept_sock;
		if (epoll_ctl(self->epollfd, EPOLL_CTL_ADD, accept_sock, &ev)) {
			perror("epoll_ctl: accept_sock");
			return 0;
		}
		index += 1;
		goto re_loop;
	}
	accept_sock = events[index].data.fd;
	setsockopt(accept_sock, SOL_SOCKET, SO_SNDTIMEO, &timeout,
		   sizeof(timeout));
	raw_object = event_raw_new(0);
	do {
		bytes = recv(accept_sock, msg_buffer, 8193, 0);
		if (bytes < 0)
			perror("recv");
		if (bytes > 0)
			event_raw_add_data(raw_object, bytes, msg_buffer);
		if (bytes != 8193)
			break;
	} while (bytes > 0);

	raw = event_raw_get_data(raw_object);
	if (raw)
		list = event_list_new_with_raw(raw);
	else
		list = NULL;
	event_object_unref(raw_object);

	if (!list) {
		if (close(accept_sock) == -1)
			perror("close");
		event_debug(
			NetworkObject, network_object_loop_wait, "已关闭: '%s'",
			inet_ntop(AF_INET,
				  &((struct sockaddr_in *)&address)->sin_addr,
				  msg_buffer, addr_len));
		index += 1;
		goto re_loop;
	}
	q_node = execute_queue_new();
	execute_queue_set(q_node, accept_sock, list);
	self->accept_handler(self->handler_object, q_node);
	index += 1;
	goto re_loop;
}

static void
real_send(NetworkObject *self, EventList *command, int sock)
{
	EventList     *response_node;
	EventRaw      *raw_object;
	epointer       raw;
	euint32	       raw_size;
	NetworkRetType nr_type;
	int	       status;

	/* 取得服务器返回类型 */
	nr_type = (NetworkRetType)EN_GET_D(command, euint64);
	event_debug(NetworkObject, real_send,
		    "服务器返回: %d (0未知，1关闭，2正常)", nr_type);
	if (nr_type != NETWORK_RET_TYPE_NORMAL) {
		close(sock);
		return;
	}
	response_node = EN_GET_NEXT(command, EventList *);
	raw_object    = event_list_export_raw(response_node);
	//
	raw	 = event_raw_get_data(raw_object);
	raw_size = event_raw_get_size(raw_object);
	setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
	status = send(sock, raw, (size_t)raw_size, 0);
	if (status != raw_size) {
		perror("send");
		close(sock);
	}
	event_object_unref(raw_object);
}

void
network_object_add_task(NetworkObject *self, ExecuteQueue *q_node)
{
	return_if_fail(self != NULL, network_object_add_task);
	return_if_fail(q_node != NULL, network_object_add_task);

	real_send(self, execute_queue_get_command(q_node),
		  execute_queue_get_sock(q_node));
	event_object_unref(q_node);
}

void
network_object_signal_connect_accept(
	NetworkObject *self,
	void (*accept_handler)(EventObject *handler, ExecuteQueue *q_node),
	EventObject *handler)
{
	return_if_fail(self != NULL, network_object_signal_connect_accept);
	self->accept_handler = accept_handler;
	self->handler_object = handler;
}
